package com.gwtserializer.dispatch.annotation.processor;

import static javax.lang.model.SourceVersion.RELEASE_6;

import java.io.IOException;
import java.io.Writer;
import java.util.Collection;

import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;

import com.gwtserializer.dispatch.annotation.GenSerializer;
import com.gwtserializer.dispatch.annotation.helper.GenHelper;
import com.gwtserializer.dispatch.annotation.helper.ReflectionHelper;

/**
 * Processes {@link GenSerializer} annotations.
 * <p/>
 * {@link GenSerializerProcessor} should only ever be called by tool
 * infrastructure. See {@link javax.annotation.processing.Processor} for more
 * details.
 * 
 * @author Fredy Tuco
 */
 
@SupportedAnnotationTypes("com.gwtserializer.dispatch.annotation.GenSerializer")
@SupportedSourceVersion(RELEASE_6)
public class GenSerializerProcessor extends GenProcessor {

	@Override
	public void process(Element customElement) {
		GenHelper writer = null;
		try {
			ReflectionHelper reflection = new ReflectionHelper(getEnvironment(), (TypeElement) customElement);
			String customElementSimpleName = reflection.getSimpleClassName();
			String customSimpleName = customElementSimpleName + "_CustomFieldSerializer";
			String customClassName = reflection.getClassName() + "_CustomFieldSerializer";
			String customSuperName = customElementSimpleName;

			printMessage("Generating '" + customClassName + "' from '" + customElementSimpleName + "'.");

			Writer sourceWriter = getEnvironment().getFiler().createSourceFile(customClassName, customElement).openWriter();
			writer = new GenHelper(sourceWriter);

			Collection<VariableElement> orderedElementFields = reflection.getFields();
			
			writer.generatePackageDeclaration(reflection.getPackageName());
			writer.generateImports("com.google.gwt.user.client.rpc.SerializationException",
					"com.google.gwt.user.client.rpc.SerializationStreamReader",
					"com.google.gwt.user.client.rpc.SerializationStreamWriter");
			writer.generateClassHeader(customSimpleName, null, reflection.getClassRepresenter().getModifiers());			
			generateDeserializeMethod(writer, customSuperName, orderedElementFields);
			generateSerializeMethod(writer, customSuperName, orderedElementFields);			
			writer.generateFooter();

		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			if (writer != null) {
				writer.close();
			}
		}

	}
	
	public void generateDeserializeMethod(GenHelper writer, String simpleClassName, Collection<VariableElement> fieldElements) {

		writer.println();
		writer.println();
		for (VariableElement variableElement : fieldElements) {
			if(writer.isCollection(variableElement.asType())){
				writer.println("  @SuppressWarnings(\"unchecked\") ");
				break;
			}
		}		
		writer.println("  public static void deserialize(SerializationStreamReader reader, {0} instance) throws SerializationException {", simpleClassName);
		writer.println();
		if (fieldElements.size() > 0) {
			for (VariableElement fieldElement : fieldElements) {
				TypeMirror type = fieldElement.asType();
				String name = writer.capitalize(fieldElement.getSimpleName().toString());
				if (writer.isCollection(type)) {
					writer.println("       instance.set{0}(({1})reader.readObject());", name, type.toString());
				} else if (type.toString().contains("String")) {
					writer.println("       instance.set{0}(reader.read{1}());", name, writer.determineType(type));
				} else if (writer.isPrimitive(type)) {
					writer.println("       instance.set{0}(reader.read{1}());", name, writer.determineType(type));
				} else {
					writer.println("       instance.set{0}(({1})reader.readObject());", name, type.toString());
				}
			}
		}
		writer.println();
		writer.println("  }");

	}

	public void generateSerializeMethod(GenHelper writer, String simpleClassName, Collection<VariableElement> fieldElements) {

		writer.println();
		writer.println("  public static void serialize(SerializationStreamWriter writer, {0} instance) throws SerializationException {",
				simpleClassName);
		writer.println();
		if (fieldElements.size() > 0) {
			for (VariableElement fieldElement : fieldElements) {
				TypeMirror type = fieldElement.asType();
				String name = writer.capitalize(fieldElement.getSimpleName().toString());
				if (writer.isCollection(type)) {
					writer.println("       if (instance.get{0}() == null) {", name);
					writer.println("          writer.writeObject(new {0}());", writer.getInstanceCollection(type));
					writer.println("       } else {");
					writer.println("          writer.writeObject(new {0}(instance.get{1}()));", writer.getInstanceCollection(type), name);
					writer.println("       }");
				} else if (type.toString().contains("String")) {
					writer.println("       writer.write{0}(instance.{1}());", writer.determineType(type), writer.determineAccessorName(fieldElement));
				} else if (writer.isPrimitive(type)) {
					writer.println("       writer.write{0}(instance.{1}());", writer.determineType(type), writer.determineAccessorName(fieldElement));
				} else {
					writer.println("       writer.writeObject(instance.{0}());", writer.determineAccessorName(fieldElement));
				}
			}
		}
		writer.println();
		writer.println("  }");
		writer.println();
	}
	
}
